#include "net.h"
#include "icmp.h"
#include "ip.h"
#include "udp.h"


/**
 * @brief 发送icmp响应
 * 
 * @param req_buf 收到的icmp请求包
 * @param src_ip 源ip地址
 */
static void icmp_resp(buf_t *req_buf, uint8_t *src_ip)
{
    // 初始化ICMP查询报文
    buf_init(&txbuf, req_buf->len);

    // 数据部分直接拷贝请求报文 修改type和重算校验和即可
    memcpy(txbuf.data, req_buf->data, req_buf->len);
    icmp_hdr_t* icmph = (icmp_hdr_t*)txbuf.data;
    icmph->type = ICMP_TYPE_ECHO_REPLY;
    icmph->checksum16 = 0;
    icmph->checksum16 = checksum16((uint16_t*)icmph, req_buf->len);

    // 交付ip协议处理
    ip_out(&txbuf, src_ip, NET_PROTOCOL_ICMP);
}

/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_ip 源ip地址
 */
void icmp_in(buf_t *buf, uint8_t *src_ip)
{
    // 如果接受到的包长小于ICMP头长度，则丢弃之
    if(buf->len < sizeof(icmp_hdr_t))       return;

    // 如果报文类型是回显请求则进行ping应答 目前只需要处理ping的应答
    icmp_hdr_t* icmph = (icmp_hdr_t*) buf->data;
    if(icmph->type == ICMP_TYPE_ECHO_REQUEST)   return icmp_resp(buf, src_ip);
}

/**
 * @brief 发送icmp不可达 
 * 
 * @param recv_buf 收到的ip数据包
 * @param src_ip 源ip地址
 * @param code icmp code，协议不可达或端口不可达
 */
void icmp_unreachable(buf_t *recv_buf, uint8_t *src_ip, icmp_code_t code)
{
    // 初始化ICMP差错报文 其结构要带上IP数据包头部及IP数据段的前8个字节（UDP/TCP报头的信息字段）
    buf_init(&txbuf, sizeof(udp_hdr_t) + sizeof(ip_hdr_t));
    memcpy(txbuf.data, recv_buf->data, sizeof(udp_hdr_t) + sizeof(ip_hdr_t));

    // 添加ICMP报头 并填写信息
    buf_add_header(&txbuf, sizeof(icmp_hdr_t));
    icmp_hdr_t* icmph = (icmp_hdr_t*) txbuf.data;
    icmph->type = ICMP_TYPE_UNREACH;
    icmph->code = code;
    icmph->id16 = 0;
    icmph->seq16 = 0;

    // 最后计算校验和
    icmph->checksum16 = 0;
    icmph->checksum16 = checksum16((uint16_t*)icmph, txbuf.len);

    // 交付给ip协议发送
    ip_out(&txbuf, src_ip, NET_PROTOCOL_ICMP);
}

/**
 * @brief 初始化icmp协议
 * 
 */
void icmp_init(){
    net_add_protocol(NET_PROTOCOL_ICMP, icmp_in);
}